// file: kernel/driver.c
// autor: jiangxinpneg
// time： 2021.10.12
// copyright: (C) 2020-2050 by jiangxinpeng,All right are reserved.

#include <os/driver.h>
#include <os/mdl.h>
#include <os/spinlock.h>
#include <os/fsal.h>
#include <os/debug.h>
#include <os/memcache.h>
#include <os/walltime.h>
#include <os/memspace.h>
#include <os/mutexlock.h>
#include <os/file.h>
#include <os/dir.h>
#include <os/dirent.h>
#include <arch/page.h>
#include <os/vmm.h>
#include <os/driver.h>
#include <os/schedule.h>
#include <os/virmem.h>
#include <os/fsal.h>
#include <os/path.h>
#include <os/file.h>
#include <os/dir.h>
#include <sys/res.h>
#include <lib/list.h>
#include <sys/ioctl.h>
#include <lib/string.h>
#include <lib/type.h>
#include <lib/errno.h>
#include <lib/unistd.h>

// globol driver list head
static LIST_HEAD(driver_list_head);
// device handle table
static device_object_t *device_handle_table[DEVICE_NUM_MAX];
// driver spinlock
static DEFINE_SPIN_LOCK(driver_lock);
// device fsal interface
fsal_t devfs_fsal;
// dev path
#define DEVFS_PATH "/dev"

// devfs date
uint32_t devfs_ctime = 0;
uint32_t devfs_cdate = 0;

// scan device for assign type
int SysScanDev(devent_t *in, device_type_t type, devent_t *out)
{
    driver_object_t *driver_obj;
    device_object_t *device_obj;
    uint8_t flags = 0;

    if (!out) // no output
        return -1;

    SpinLock(&driver_lock);
    // traversal all driver
    list_traversal_all_owner_to_next(driver_obj, &driver_list_head, list)
    {
        // traversal all device
        list_traversal_all_owner_to_next(device_obj, &driver_obj->device_list, list)
        {
            // check device type if is targe type
            if ((device_obj->type == type) || (type == DEVICE_TYPE_ANY))
            {
                if (!in)
                {
                    memset(out->dev_name, 0, DEVICE_NAME_LEN);
                    strcpy(out->dev_name, device_obj->name.text);
                    out->dev_type = type;
                    SpinUnlock(&driver_lock);
                    return 0;
                }
                else
                {
                    if (!strcmp(in->dev_name, device_obj->name.text)) // find in entry
                        flags = 1;

                    if (flags == 1)
                        if (strcmp(in->dev_name, device_obj->name.text) != 0)
                        {
                            memset(out->dev_name, 0, DEVICE_NAME_LEN);
                            strcpy(out->dev_name, device_obj->name.text);
                            out->dev_type = type;
                            SpinUnlock(&driver_lock);
                            return 0;
                        }
                }
            }
        }
    }
    SpinUnlock(&driver_lock);
    return -1;
}

iostatus_t IoCreateDevice(driver_object_t *driver, uint32_t dev_exten_size, char *devname, device_type_t type, device_object_t **device)
{
    device_object_t *devobj = KMemAlloc(sizeof(device_object_t) + dev_exten_size);
    if (!devobj)
        return IO_FAILED;
    list_init(&devobj->list);

    // create device name string
    if (string_new(&devobj->name, devname, DEVICE_NAME_LEN) < 0)
    {
        KMemFree(devobj);
        return IO_FAILED;
    }

    // create assign device object
    devobj->type = type;
    if (dev_exten_size > 0)
        // create extension area in device end
        devobj->device_extension = (void *)(devobj + 1);
    else
        // externsion no present
        devobj->device_extension = NULL;

    devobj->flags = 0;
    AtomicSet(&devobj->ref, 0);
    devobj->cur_ioreq = NULL;
    devobj->reserved = 0;

    devobj->driver = driver;
    // init device and driver lock
    SpinLockInit(&devobj->lock.spinlock);
    MutexlockInit(&devobj->lock.mutexlock);
    // set create time
    devobj->mtime = WALLTIME_WR_TIME(walltime.hour, walltime.minute, walltime.second);
    devobj->mdate = WALLTIME_WR_DATE(walltime.year, walltime.month, walltime.day);
    // check if device obj had present in driver device list
    if (!list_find(&devobj->list, &driver->device_list))
    {
        list_add_tail(&devobj->list, &driver->device_list);
    }
    // return devobj point
    *device = devobj;

    return IO_SUCCESS;
}

int IoDeleteDevice(device_object_t *device)
{
    device_object_t *devobj;
    driver_object_t *driver;

    if (!device)
        return -1;

    SpinLock(&driver_lock);
    devobj = DeviceHandleTableFindByName(device->name.text);
    // if device in handle table
    if (devobj)
    {
        KPrint(PRINT_INFO "IoDeleteDevice: device %s is using\n", devobj->name.text);
        // device already open,uninstall force.
        DeviceHandleTableRemove(devobj);
    }
    SpinUnlock(&driver_lock);

    // del devobj from driver list
    // point device object
    devobj = device;
    driver = devobj->driver;
    if (!driver)
    {
        KPrint(PRINT_ERR "IoDeleteDevice: device %s no can delete!\n", devobj->name.text);
        return -1;
    }

    SpinLock(&driver->device_lock);
    // find device object in driver object device list
    if (list_find(&devobj->list, &driver->device_list))
    {
        list_del(&devobj->list);
        string_del(&devobj->name);
        KMemFree(devobj);
    }
    SpinUnlock(&driver->device_lock);
    return 0;
}

// initilization device queue
void IoDeviceQueueInit(device_queue_t *queue)
{
    SpinLockInit(&queue->lock);
    list_init(&queue->list_head);
    WaitQueueInit(&queue->wait_queue);
    queue->count = 0;
}

void IoDeviceQueueCleanup(device_queue_t *queue)
{
    device_queue_entry_t *entry, *next;
    SpinLock(&queue->lock);
    list_traversal_all_owner_to_next_safe(entry, next, &queue->list_head, list)
    {
        list_del(&entry->list);
        KMemFree(entry);
    }
    SpinUnlock(&queue->lock);
}

iostatus_t IoDeviceQueueAppend(device_queue_t *queue, uint8_t *buff, int len)
{
    SpinLock(&queue->lock);
    if (queue->count > DEVICE_QUEUE_ENTRY_NUM)
    {
        SpinUnlock(&queue->lock);
        return IO_FAILED;
    }
    // alloc new queue entry
    device_queue_entry_t *entry = KMemAlloc(sizeof(device_queue_entry_t) + len);
    if (!entry)
    {
        SpinUnlock(&queue->lock);
        return IO_FAILED;
    }
    list_add_tail(&entry->list, &queue->list_head);
    queue->count++;
    entry->buff = (uint8_t *)(entry + 1);
    entry->length = len;
    memcpy(entry->buff, buff, len);
    SpinUnlock(&queue->lock);
    WaitQueueWakeup(&queue->wait_queue); // wakup other waiter
    return IO_SUCCESS;
}

int IoDeviceQueuePickup(device_queue_t *queue, uint8_t *buff, uint32_t bufflen, int flags)
{
    SpinLock(&queue->lock);
    if (!queue->count) // no entry
    {
        if (flags & IO_NOWAITTING) // no wait just direct exit
        {
            SpinUnlock(&queue->lock);
            return 0;
        }
        // add task to waitqueue and block
        WaitQueueAdd(&queue->wait_queue, cur_task);
        SpinUnlock(&queue->lock);
        TaskBlock(TASK_BLOCKED);
        SpinLock(&queue->lock);
    }
    device_queue_entry_t *entry;
    entry = list_first_owner(&queue->list_head, device_queue_entry_t, list);
    list_del(&entry->list);
    queue->count--;
    int len = min(entry->length, bufflen);
    memcpy(buff, entry->buff, len);
    KMemFree(entry);
    SpinUnlock(&queue->lock);
    return len;
}

void DriverObjectInit(driver_object_t *driver)
{
    list_init(&driver->list);
    list_init(&driver->device_list);
    driver->driver_enter = NULL;
    driver->driver_exit = NULL;
    driver->driver_extension = NULL;
    for (int i = 0; i < IOREQ_FUN_MAX; i++)
    {
        driver->dispatch_fun[i] = DefaultDeviceDispatch;
    }
    string_init(&driver->name);
    SpinLockInit(&driver->device_lock);
}

int DriverObjectCreate(driver_fun_t fun)
{
    driver_object_t *driver_obj;
    iostatus_t status;

    // alloc driver object
    driver_obj = (driver_object_t *)KMemAlloc(sizeof(driver_object_t));
    if (!driver_obj)
    {
        KPrint("[driver] alloc driver object faild!\n");
        return -1;
    }
    DriverObjectInit(driver_obj);

    // exec driver function
    status = fun(driver_obj);
    if (status != IO_SUCCESS)
    {
        KPrint("[driver] exec driver func failed!\n");
        KMemFree(driver_obj);
        return -1;
    }

    // doing driver enter function if is present
    if (driver_obj->driver_enter)
    {
        status = driver_obj->driver_enter(driver_obj);
        if (status != IO_SUCCESS)
        {
            KPrint("[driver] driver enter function error!\n");
            KMemFree(driver_obj);
            return -1;
        }
    }

    SpinLock(&driver_lock);
    // add driver object to list
    if (!list_find(&driver_obj->list, &driver_list_head))
    {
        list_add_tail(&driver_obj->list, &driver_list_head);
    }
    SpinUnlock(&driver_lock);

    KPrint("[driver] driver object %s create\n", driver_obj->name.text);
    return 0;
}

int DriverObjectDel(driver_object_t *driver)
{
    iostatus_t status;
    if (driver->driver_exit)
    {
        status = driver->driver_exit(driver);
    }
    if (status != IO_SUCCESS)
        return -1;
    SpinLock(&driver_lock);
    if (list_find(&driver->list, &driver_list_head))
    {
        // del driver object from driver list
        list_del(&driver->list);
        KMemFree(driver);
    }
    SpinUnlock(&driver_lock);
    return status;
}

iostatus_t DefaultDeviceDispatch(device_object_t *devobj, io_request_t *ioreq)
{
    ioreq->io_status.info = 0;
    ioreq->io_status.status = IO_SUCCESS;
    IoCompleteRequest(ioreq);
}

// alloc mem for a ioreq
io_request_t *IoRequireAlloc()
{
    io_request_t *ioreq = KMemAlloc(sizeof(io_request_t));
    if (ioreq != NULL)
        memset(ioreq, 0, sizeof(io_request_t));
    return ioreq;
}

// free mem from a ioreq
void IoRequireFree(io_request_t *ioreq)
{
    KMemFree(ioreq);
}

// device inc reference
iostatus_t IoDeviceIncRef(device_object_t *devobj)
{
    if (AtomicGet(&devobj->ref) >= 0)
    {
        AtomicInc(&devobj->ref);
        return IO_SUCCESS;
    }
    else
    {
        KPrint(PRINT_ERR "device open:referenct %d error!\n", AtomicGet(&devobj->ref));
        return IO_FAILED;
    }
}

// device dec reference
iostatus_t IoDeviceDecRef(device_object_t *devobj)
{
    if (AtomicGet(&devobj->ref) > 0)
    {
        AtomicDec(&devobj->ref);
        return IO_SUCCESS;
    }
    else
    {
        KPrint(PRINT_ERR "device open: referenct %d error!\n", AtomicGet(&devobj->ref));
        return IO_FAILED;
    }
}

// check ioreq whether had completed
int IoCompleteCheck(io_request_t *ioreq, iostatus_t status)
{
    if (status == IO_SUCCESS)
    {
        if (ioreq->io_status.status == IO_SUCCESS && ioreq->flags & IOREQ_COMPLETION)
            return 0;
    }
    return -1;
}

// search device object by the device name
device_object_t *IoSearchDeviceByName(char *devname)
{
    device_object_t *devobj;
    driver_object_t *driver;
    devobj = DeviceHandleTableFindByName(devname);
    if (devobj != NULL)
    {
        return devobj;
    }
    list_traversal_all_owner_to_next(driver, &driver_list_head, list)
    {
        list_traversal_all_owner_to_next(devobj, &driver->device_list, list)
        {
            if (!strcmp(devobj->name.text, devname))
            {
                return devobj;
            }
        }
    }
    return NULL;
}

// search driver object by the driver name
driver_object_t *IoSearchDriverByName(char *name)
{
    driver_object_t *driver;
    SpinLock(&driver_lock);
    list_traversal_all_owner_to_next(driver, &driver_list_head, list)
    {
        // check if is targe driver object
        if (!strcmp(driver->name.text, name))
        {
            SpinUnlock(&driver_lock);
            return driver;
        }
    }
    return NULL;
}

io_request_t *IoBuildSyncRequire(uint64_t fun, device_object_t *devobj, char *devname, void *buff, uint32_t len, uint32_t off, uint32_t flags, int code, uint32_t arg, io_status_block_t *iostatus_block)
{
    io_request_t *ioreq = IoRequireAlloc();
    if (!ioreq)
    {
        return NULL;
    }
    ioreq->devobj = devobj;
    if (iostatus_block)
    {
        ioreq->io_status = *iostatus_block;
    }
    else
    {
        ioreq->io_status.info = 0;
        ioreq->io_status.status = IO_FAILED;
    }
    // set ioreq
    list_init(&ioreq->list);
    ioreq->sys_buff = NULL;
    ioreq->user_buff = buff;
    ioreq->mdl_list = NULL;
    if (buff)
    {
        // device buffer io
        if (devobj->flags & DEVICE_BUFFER_IO)
        {
            if (len >= MEM_CACHE_SIZE_MAX)
            {
                len = MEM_CACHE_SIZE_MAX;
                KPrint(PRINT_WARNNING "IoBuildSnycRequire: len=%x too int64_t\n", len);
            }
            // alloc system buffer
            ioreq->sys_buff = KMemAlloc(len);
            if (!ioreq->sys_buff)
            {
                KMemFree(ioreq);
                return NULL;
            }
            ioreq->flags |= IOREQ_BUFFER_IO;
        }
        else
        {
            // device direct io
            if (devobj->flags & DEVICE_DIRECT_IO)
            {
                // alloc mem descript list
                // director use user addr
                ioreq->mdl_list = MdlAlloc((uint32_t)buff, len, ioreq);
                if (!ioreq->mdl_list)
                {
                    KMemFree(ioreq);
                    return NULL;
                }
            }
        }
    }
    // function
    switch (fun)
    {
    case IOREQ_OPEN:
        ioreq->flags |= IOREQ_OPEN_OPERATOR;
        ioreq->parame.open.devname = devname;
        ioreq->parame.open.flags = 0;
        break;
    case IOREQ_CLOSE:
        ioreq->flags |= IOREQ_CLOSE_OPERATOR;
        break;
    case IOREQ_READ:
        ioreq->flags |= IOREQ_READ_OPERATOR;
        ioreq->parame.read.len = len;
        ioreq->parame.read.offset = off;
        break;
    case IOREQ_WRITE:
        ioreq->flags |= IOREQ_WRITE_OPERATOR;
        ioreq->parame.write.len = len;
        ioreq->parame.write.offset = off;
        if (devobj->flags & DEVICE_BUFFER_IO)
        {
            // copy user buffer to system buffer
            memcpy(ioreq->sys_buff, ioreq->user_buff, len);
        }
        break;
    case IOREQ_DEVCTL:
        ioreq->flags |= IOREQ_DEVCTL_OPERATOR;
        ioreq->parame.devctl.code = code;
        ioreq->parame.devctl.arg = arg;
        break;
    case IOREQ_MMAP:
        ioreq->flags |= IOREQ_MMAP_OPERATOR;
        ioreq->parame.mmap.flags = flags;
        ioreq->parame.mmap.len = len;
        break;
    default:
        break;
    }
    return ioreq;
}

iostatus_t IoCallDriver(device_object_t *devobj, io_request_t *ioreq)
{
    iostatus_t status = IO_FAILED;
    driver_dispatch_t fun = NULL;
    // according device type choice lock
    switch (devobj->type)
    {
    case DEVICE_TYPE_BEEP:
    case DEVICE_TYPE_SCREEN:
    case DEVICE_TYPE_KEYBOARD:
    case DEVICE_TYPE_MOUSE:
    case DEVICE_TYPE_VIRTUAL_CHAR:
    case DEVICE_TYPE_SERIAL:
    case DEVICE_TYPE_VIEW:
    case DEVICE_TYPE_SOUND:
        SpinLock(&devobj->lock.spinlock);
        break;
    case DEVICE_TYPE_VOL:
    case DEVICE_TYPE_DISK:
    case DEVICE_TYPE_VIRTUAL_DISK:
    case DEVICE_TYPE_NETWORK:
    case DEVICE_TYPE_PHYSIC_NETWORKCARD:
        MutexlockLock(&devobj->lock.mutexlock, MUTEX_LOCK_MODE_BLOCK);
        break;
    default:
        break;
    }
    // according ioreq flags type to get driver fun
    if (ioreq->flags & IOREQ_OPEN_OPERATOR)
    {
        fun = devobj->driver->dispatch_fun[IOREQ_OPEN];
    }
    else
    {
        if (ioreq->flags & IOREQ_CLOSE_OPERATOR)
        {
            fun = devobj->driver->dispatch_fun[IOREQ_CLOSE];
        }
        else
        {
            if (ioreq->flags & IOREQ_READ_OPERATOR)
            {
                fun = devobj->driver->dispatch_fun[IOREQ_READ];
            }
            else
            {
                if (ioreq->flags & IOREQ_WRITE_OPERATOR)
                {
                    fun = devobj->driver->dispatch_fun[IOREQ_WRITE];
                }
                else
                {
                    if (ioreq->flags & IOREQ_DEVCTL_OPERATOR)
                    {
                        fun = devobj->driver->dispatch_fun[IOREQ_DEVCTL];
                    }
                    else
                    {
                        if (ioreq->flags & IOREQ_MMAP_OPERATOR)
                        {
                            fun = devobj->driver->dispatch_fun[IOREQ_MMAP];
                        }
                    }
                }
            }
        }
    }
    // call dispatch fun and return io status
    if (fun)
    {
        status = fun(devobj, ioreq);
    }
    return status;
}

void IoCompleteRequest(io_request_t *ioreq)
{
    if (ioreq->io_status.status == IO_FAILED)
    {
        ioreq->io_status.info = -1;
    }
    ioreq->flags |= IOREQ_COMPLETION;
    // according device object choice lock
    switch (ioreq->devobj->type)
    {
    case DEVICE_TYPE_BEEP:
    case DEVICE_TYPE_SCREEN:
    case DEVICE_TYPE_KEYBOARD:
    case DEVICE_TYPE_MOUSE:
    case DEVICE_TYPE_VIRTUAL_CHAR:
    case DEVICE_TYPE_SERIAL:
    case DEVICE_TYPE_VIEW:
    case DEVICE_TYPE_SOUND:
        SpinUnlock(&ioreq->devobj->lock.spinlock);
        break;
    case DEVICE_TYPE_VOL:
    case DEVICE_TYPE_DISK:
    case DEVICE_TYPE_VIRTUAL_DISK:
    case DEVICE_TYPE_NETWORK:
    case DEVICE_TYPE_PHYSIC_NETWORKCARD:
        MutexlockUnlock(&ioreq->devobj->lock.mutexlock);
        break;
    default:
        break;
    }
}

device_object_t *DeviceHandleTableFindByName(char *devname)
{
    device_object_t *devobj;
    for (int i = 0; i < DEVICE_NUM_MAX; i++)
    {
        devobj = device_handle_table[i];
        if (devobj != NULL)
        {
            if (!strcmp(devobj->name.text, devname))
            {
                return devobj;
            }
        }
    }
    return NULL;
}

int DeviceHandleTableFindByObject(device_object_t *device)
{
    device_object_t *devobj;
    for (int i = 0; i < DEVICE_NUM_MAX; i++)
    {
        devobj = device_handle_table[i];
        if (devobj == device)
            return i;
    }
    return -1;
}

int DeviceHandleTableInsert(device_object_t *devobj)
{
    int i;
    device_object_t **device;

    for (i = 0; i < DEVICE_NUM_MAX; i++)
    {
        // get targe object point
        device = &device_handle_table[i];
        if (!*device)
        {
            // insert point
            *device = devobj;
            return i;
        }
    }
    return -1;
}

int DeviceHandleTableRemove(device_object_t *devobj)
{
    device_object_t **device;

    for (int i = 0; i < DEVICE_NUM_MAX; i++)
    {
        device = &device_handle_table[i];
        if (*device != NULL)
        {
            if (!strcmp((*device)->name.text, devobj->name.text) && ((*device)->type == devobj->type))
            {
                *device = NULL;
                return 0;
            }
        }
    }
    return -1;
}

device_handle_t DeviceOpen(char *dev, int flags)
{
    device_object_t *devobj = IoSearchDeviceByName(dev);
    iostatus_t status;
    io_request_t *ioreq;
    int handle;

    if (!devobj)
    {
        KPrint(PRINT_ERR "device open: device %s not find\n", dev);
        return -1;
    }
    // inc device ref
    status = IoDeviceIncRef(devobj);
    if (status == IO_FAILED)
    {
        KPrint(PRINT_ERR "device open: increase reference faild\n");
        return -1;
    }
    // first reference device
    if (AtomicGet(&devobj->ref) == 1)
    {
        ioreq = IoBuildSyncRequire(IOREQ_OPEN, devobj, NULL, NULL, 0, 0, 0, 0, 0, NULL);
        if (!ioreq)
        {
            KPrint(PRINT_ERR "DeviceOpen: alloc ioreq faild\n");
            DEVICE_DISPATCH_FAILED();
            return -1;
        }
        ioreq->parame.open.devname = dev;
        ioreq->parame.open.flags = flags;
        status = IoCallDriver(devobj, ioreq);
        // check ioreq is  complete
        if (!IoCompleteCheck(ioreq, status))
        {
            IoRequireFree(ioreq);
            SpinLock(&driver_lock);
            // insert handle and return device handle
            handle = DeviceHandleTableInsert(devobj);
            if (handle != -1)
            {
                SpinUnlock(&driver_lock);
                return handle;
            }
            KPrint(PRINT_ERR "DeviceOpen: insert device handle faild\n");
            SpinUnlock(&driver_lock);
            return -1;
        }
        // ioreq faild
        IoRequireFree(ioreq);
        DEVICE_DISPATCH_FAILED();
        return -1;
    }
    else
    {
        // ioreq had present
        // direct return handle
        handle = DeviceHandleTableFindByObject(devobj);
        return handle;
    }
}

int DeviceClose(device_handle_t handle)
{
    device_object_t *devobj;
    iostatus_t status;
    io_request_t *ioreq;

    if (IS_BAD_DEVICE_HANDLE(handle))
        return -1;
    devobj = GET_DEVICE_BY_HANDLE(handle);
    if (!devobj)
    {
        KPrint(PRINT_ERR "DeviceClose: device object error by handle=%d!\n", handle);
        return -1;
    }
    status = IoDeviceDecRef(devobj);
    if (status == IO_FAILED)
    {
        return -1;
    }
    // only last can close
    if (!AtomicGet(&devobj->ref))
    {
        ioreq = IoBuildSyncRequire(IOREQ_CLOSE, devobj, NULL, NULL, 0, 0, 0, 0, 0, NULL);

        if (!ioreq)
        {
            KPrint(PRINT_ERR "DeviceClose: alloc ioreq object failed!\n");
            IoDeviceIncRef(devobj);
            return -1;
        }
        // call driver function
        status = IoCallDriver(devobj, ioreq);
        if (!IoCompleteCheck(ioreq, status))
        {
            IoRequireFree(ioreq);
            SpinLock(&driver_lock);
            if (DeviceHandleTableRemove(devobj) < 0)
            {
                KPrint(PRINT_ERR "DeviceClose: device remove from device handle table failed!\n");
                IoDeviceIncRef(devobj); // rollback
                SpinUnlock(&driver_lock);
                return -1;
            }
            SpinUnlock(&driver_lock);
            return 0;
        }
        IoRequireFree(ioreq);
        IoDeviceIncRef(devobj); // rollback
        return -1;
    }
    else
    {
        // if no is last close,do direct back
        return 0;
    }
}

int DeviceIncRef(int handle)
{
    device_object_t *devobj;
    iostatus_t status;
    if (IS_BAD_DEVICE_HANDLE(handle))
    {
        return -1;
    }
    devobj = GET_DEVICE_BY_HANDLE(handle);
    if (!devobj)
    {
        KPrint(PRINT_ERR "DeviceIncRef: device object error by handle=%d !\n", handle);
        return -1;
    }
    // device io interface
    status = IoDeviceIncRef(devobj);
    if (status == IO_SUCCESS)
        return 0;
    return -1;
}

int DeviceDecRef(int handle)
{
    device_object_t *devobj;
    iostatus_t status;
    if (IS_BAD_DEVICE_HANDLE(handle))
    {
        return -1;
    }
    devobj = GET_DEVICE_BY_HANDLE(handle);
    if (!devobj)
    {
        KPrint(PRINT_ERR "DeviceIncRef: device object error by handle=%d!\n", handle);
        return -1;
    }
    // device io interface
    status = IoDeviceDecRef(devobj);
    if (status == IO_SUCCESS)
        return 0;
    return -1;
}

int DeviceRead(device_handle_t handle, void *buff, size_t size, offset_t off)
{
    device_object_t *devobj;
    io_request_t *ioreq;
    iostatus_t status;
    uint32_t len = 0;

    if (IS_BAD_DEVICE_HANDLE(handle))
        return -1;
    devobj = GET_DEVICE_BY_HANDLE(handle);
    if (!devobj)
    {
        KPrint(PRINT_ERR "DeviceRead: device object error by handle=%d\n!", handle);
        return -1;
    }
    // build request
    ioreq = IoBuildSyncRequire(IOREQ_READ, devobj, NULL, buff, size, off, 0, 0, 0, NULL);
    if (!ioreq)
    {
        KPrint(PRINT_ERR "DeviceRead: alloc ioreq object failed!\n");
        return -1;
    }
    // call driver
    status = IoCallDriver(devobj, ioreq);
    if (!IoCompleteCheck(ioreq, status))
    {
        len = ioreq->io_status.info;
        // device buffer io
        if (devobj->flags & DEVICE_BUFFER_IO)
        {
            InterruptDisable();
            memcpy(ioreq->user_buff, ioreq->sys_buff, len);
            InterruptEnable();
            KMemFree(ioreq->sys_buff); // free buffer
        }
        else
        {
            // direct io
            if (devobj->flags & DEVICE_DIRECT_IO)
            {
                KPrint(PRINT_ERR "DeviceRead: Read finish,free mdl\n");
                KMemFree(ioreq->mdl_list);
                ioreq->mdl_list = NULL;
            }
        }
        IoRequireFree(ioreq);
        return len;
    }
    IoRequireFree(ioreq);
    return -1;
}

int DeviceWrite(device_handle_t handle, void *buff, size_t size, offset_t off)
{
    device_object_t *devobj;
    io_request_t *ioreq;
    iostatus_t status;
    uint32_t len;

    if (IS_BAD_DEVICE_HANDLE(handle))
        return -1;
    devobj = GET_DEVICE_BY_HANDLE(handle);
    if (!devobj)
    {
        KPrint(PRINT_ERR "DeviceRead: device object error by handle=%d\n!", handle);
        return -1;
    }
    // create ioreq
    ioreq = IoBuildSyncRequire(IOREQ_WRITE, devobj, NULL, buff, size, off, 0, 0, 0, NULL);
    if (!ioreq)
    {
        KPrint(PRINT_ERR "DeviceRead: alloc ioreq object failed!\n");
        return -1;
    }
    // call driver interface
    status = IoCallDriver(devobj, ioreq);
    if (!IoCompleteCheck(ioreq, status))
    {
        len = ioreq->io_status.info;
        // if device direct io
        // free mdl list
        if (devobj->flags & DEVICE_DIRECT_IO)
        {
            KMemFree(ioreq->mdl_list);
            ioreq->mdl_list = NULL;
        }
        // rollback ioreq
        IoRequireFree(ioreq);
        return len;
    }
    IoRequireFree(ioreq);
    return -1;
}

int DeviceDevCtl(device_handle_t handle, int cmd, void *arg)
{
    device_object_t *devobj;
    iostatus_t status;
    io_request_t *ioreq;

    if (IS_BAD_DEVICE_HANDLE(handle))
        return -1;
    devobj = GET_DEVICE_BY_HANDLE(handle);
    if (!devobj)
    {
        KPrint(PRINT_ERR "DeviceCtl: device object error by handle=%d\n", handle);
        return -1;
    }
    ioreq = IoBuildSyncRequire(IOREQ_DEVCTL, devobj, NULL, NULL, 0, 0, 0, cmd, (uint32_t)arg, NULL);
    if (!ioreq)
    {
        KPrint(PRINT_ERR "DeviceCtl: ioreq object alloc failed!\n");
        return -1;
    }
    // call driver
    status = IoCallDriver(devobj, ioreq);
    if (!IoCompleteCheck(ioreq, status))
    {
        IoRequireFree(ioreq);
        return 0;
    }
    IoRequireFree(ioreq);
    return -1;
}

void *DeviceMMap(device_handle_t handle, size_t len, int flags)
{
    device_object_t *devobj;
    iostatus_t status;
    io_request_t *ioreq;
    void *map_addr;

    if (IS_BAD_DEVICE_HANDLE(handle))
        return NULL;
    devobj = GET_DEVICE_BY_HANDLE(handle);
    if (!devobj)
    {
        KPrint(PRINT_ERR "DeviceCtl: device object error by handle=%d\n", handle);
        return NULL;
    }
    ioreq = IoBuildSyncRequire(IOREQ_MMAP, devobj, NULL, NULL, len, 0, flags, 0, 0, NULL);
    if (!ioreq)
    {
        KPrint(PRINT_ERR "DeviceCtl: ioreq object alloc failed!\n");
        return NULL;
    }
    status = IoCallDriver(devobj, ioreq);
    if (!IoCompleteCheck(ioreq, status))
    {
        // if io status info present
        if (ioreq->io_status.info)
        {
            // kernel io
            if (flags & IO_KERNEL)
            {
                // device map to kernel space
                map_addr = MemIoReMap(ioreq->io_status.info, len);
            }
            else
            {
                switch (devobj->type)
                {
                    // virtual device
                case DEVICE_TYPE_VIEW:
                {
                    map_addr = MemSpaceMapViraddr(cur_task->vmm, 0, (uint32_t)KERNEL_PYBASE2VBASE(ioreq->io_status.info), len, PROTE_USER | PROTE_WRITE, MEM_SPACE_MAP_SHARE | MEM_SPACE_MAP_REMAP);
                }
                break;
                default:
                {
                    // defalut device
                    map_addr = MemSpaceMap(cur_task->vmm, 0, ioreq->io_status.info, len, (PROTE_READ | PROTE_WRITE), (MEM_SPACE_MAP_SHARE | MEM_SPACE_MAP_REMAP));
                }
                break;
                }
            }
            // return map addr
            IoRequireFree(ioreq);
            return map_addr;
        }
    }
    IoRequireFree(ioreq);
    return NULL;
}

void *DevFsPathTranslate(const char *path)
{
    char *p = (char *)path;

    if (!path)
        return NULL;
    // no is device path
    if (strncmp(path, DEVFS_PATH, strlen(DEVFS_PATH)) != 0)
        return NULL;
    return path + strlen(DEVFS_PATH) + 1;
}

static iostatus_t FastIoCallDriver(device_object_t *device, int arg, void *buff, int dispatch)
{
    iostatus_t status = IO_SUCCESS;
    driver_dispatch_fastio_t func = NULL;
    switch (device->type)
    {
    case DEVICE_TYPE_VIEW:
        SpinLock(&device->lock.spinlock);
        break;
    default:
        break;
    }
    func = (driver_dispatch_fastio_t)device->driver->dispatch_fun[dispatch];
    if (func)
        status = func(device, arg, buff);
    switch (device->type)
    {
    case DEVICE_TYPE_VIEW:
        SpinUnlock(&device->lock.spinlock);
        break;
    default:
        break;
    }
    return status;
}


int FastIoCall(device_handle_t handle,int arg,void *buff,int dispatch)
{
    if(IS_BAD_DEVICE_HANDLE(handle))
        return -1;
    iostatus_t status= FastIoCallDriver(GET_DEVICE_BY_HANDLE(handle),arg,buff,dispatch);
    if(status==IO_SUCCESS)
        return 0;
    else 
        return -1;
}

int InputEventInit(input_event_buff_t *event_buff)
{
    SpinLockInit(&event_buff->lock);
    event_buff->head = event_buff->tail = 0;
    event_buff->buff=KMemAlloc(sizeof(input_event_t)*EVENT_BUFF_SIZE);
    if(!event_buff->buff)
    {
        Panic("input event alloc error\n");
        return -1;
    }
    memset(event_buff->buff, 0, EVENT_BUFF_SIZE);
    return 0;
}

int InputEventPut(input_event_buff_t *event_buff, input_event_t *event)
{
    SpinLockDisInterrupt(&event_buff->lock);
    
    event_buff->buff[event_buff->tail++%EVENT_BUFF_SIZE] = *event;
    event_buff->tail &= EVENT_BUFF_SIZE - 1;
    
    SpinUnlockEnInterrupt(&event_buff->lock);
    return 0;
}

int InputEventGet(input_event_buff_t *event_buff, input_event_t *event)
{
    SpinLockDisInterrupt(&event_buff->lock);

    // event buffer queue is empty
    if (event_buff->head == event_buff->tail)
    {
        SpinUnlockEnInterrupt(&event_buff->lock);
        return -1;
    }

    *event = event_buff->buff[event_buff->head++%EVENT_BUFF_SIZE];
    event_buff->head &= EVENT_BUFF_SIZE - 1;

    SpinUnlockEnInterrupt(&event_buff->lock);
    return 0;
}

int InputEventFull(input_event_buff_t *event_buff)
{
    if (event_buff->head == (event_buff->tail + 1) % EVENT_BUFF_SIZE)
    {
        KPrint(PRINT_WARNNING "input event buffer queue full!\n");
        return 0;
    }
    return -1;
}

int InputEventEmpty(input_event_buff_t *event_buff)
{
    if (event_buff->head == event_buff->tail)
    {
        KPrint(PRINT_WARNNING "input event buffer is empty!\n");
        return 0;
    }
    return -1;
}

int DeviceProbeUnUsed(const char *name, char *buff, size_t len)
{
    driver_object_t *driver;
    device_object_t *device;
    uint32_t namelen = strlen(name);

    list_traversal_all_owner_to_next(driver, &driver_list_head, list)
    {
        list_traversal_all_owner_to_next(device, &driver->device_list, list)
        {
            if (!strncmp(name, device->name.text, namelen)) // cmp the name
            {
                if (!AtomicGet(&device->ref))
                {
                    memcpy(buff, device->name.text, min(len, strlen(device->name.text)));
                    buff[min(len,strlen(device->name.text))] = '\0';
                    KPrint("device %s probe ok\n",buff);
                    return 0;
                }
            }
        }
    }
    return -1;
}

// device abstract layer interface
static int DevIfOpen(const char *path, int flags)
{
    #ifdef DEBUG_DRIVER
    KPrint("device path open %s\n",path);
    #endif
    char *p = DevFsPathTranslate(path);
    fsal_file_t *file;
    device_handle_t handle;
    if (!p)
        return -1;
    file = FsalFileAlloc();
    file->extension = KMemAlloc(sizeof(devfs_file_extension_t));
    if (!file->extension)
    {
        KPrint(PRINT_ERR "devfs: alloc file %s extension for open failed!\n", p);
        FsalFileFree(file);
        return -ENOMEM;
    }
    file->fsal = &devfs_fsal;

    #ifdef DEBUG_DRIVER
    KPrint("driver: device %s open\n",p);
    #endif 

    handle = DeviceOpen(p, flags);
    if (handle < 0)
    {
        KMemFree(file->extension);
        FsalFileFree(file);
        return -1;
    }
    ((devfs_file_extension_t *)(file->extension))->handle = handle;
    return FSAL_FILE_FILE2IDX(file);
}

static int DevIfClose(int idx)
{
    fsal_file_t *file;
    devfs_file_extension_t *extension;
    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    file = FSAL_FILE_IDX2FILE(idx);
    extension = file->extension;
    if (extension)
        KMemFree(extension);
    file->extension = NULL;
    if (FsalFileFree(file))
        return -1;
    return DeviceClose(extension->handle);
}

static int DevIfIncRef(int idx)
{
    fsal_file_t *file;
    devfs_file_extension_t *extension;
    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    file = FSAL_FILE_IDX2FILE(idx);
    extension = file->extension;
    return DeviceIncRef(extension->handle);
}

static int DevIfDecRef(int idx)
{
    fsal_file_t *file;
    devfs_file_extension_t *extension;
    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    file = FSAL_FILE_IDX2FILE(idx);
    extension = file->extension;
    return DeviceDecRef(extension->handle);
}

static int DevIfRead(int idx, void *buff, size_t size)
{
    fsal_file_t *file;
    devfs_file_extension_t *extension;
    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    file = FSAL_FILE_IDX2FILE(idx);
    extension = file->extension;
    return DeviceRead(extension->handle, buff, size, DISKOFF_MAX);
}

static int DevIfWrite(int idx, void *buff, size_t size)
{
    fsal_file_t *file;
    devfs_file_extension_t *extension;
    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    file = FSAL_FILE_IDX2FILE(idx);
    extension = file->extension;
    return DeviceWrite(extension->handle, buff, size, DISKOFF_MAX);
}

static int DevIfIoCtl(int idx, int cmd, void *arg)
{
    fsal_file_t *file;
    devfs_file_extension_t *extension;
    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    file = FSAL_FILE_IDX2FILE(idx);
    extension = file->extension;
    return DeviceDevCtl(extension->handle, cmd, arg);
}

void *DevIfMMap(int idx, void *addr, size_t len, int prot, int flags, offset_t off)
{
    fsal_file_t *file;
    devfs_file_extension_t *extension;
    if (FSAL_FILE_BAD_IDX(idx))
        return NULL;
    file = FSAL_FILE_IDX2FILE(idx);
    extension = file->extension;
    return DeviceMMap(extension->handle, len, flags);
}

static size_t DevIfFSize(int idx)
{
    fsal_file_t *file;
    devfs_file_extension_t *extension;
    int size;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    file = FSAL_FILE_IDX2FILE(idx);
    extension = file->extension;
    if (DeviceDevCtl(extension->handle, DISKIO_GETSIZE, &size) < 0)
        return 0;
    return size;
}

static int DevIfFTell(int idx)
{
    fsal_file_t *file;
    devfs_file_extension_t *extension;
    offset_t off = 0;
    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    file = FSAL_FILE_IDX2FILE(idx);
    extension = file->extension;

    if (DeviceDevCtl(extension->handle, DISKIO_GETOFF, &off) < 0)
        return 0;
    return off;
}

static int DevIfLSeek(int idx, offset_t off, int whence)
{
    fsal_file_t *file;
    devfs_file_extension_t *extension;
    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    file = FSAL_FILE_IDX2FILE(idx);
    extension = file->extension;
    return DeviceDevCtl(extension->handle, DISKIO_SETOFF, &off);
}

static int DevIfFastIo(int idx, int cmd, void *arg)
{
    fsal_file_t *file;
    devfs_file_extension_t *extension;
    device_object_t *devobj;
    iostatus_t status;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    file = FSAL_FILE_IDX2FILE(idx);
    if (!file)
        return -1;
    extension=file->extension;
    devobj = GET_DEVICE_BY_HANDLE(extension->handle);
    if (!devobj)
        return -1;
    status = FastIoCallDriver(devobj, cmd, arg, IOREQ_FASTIO);
    if (status != IO_SUCCESS)
        return -1;
    return 0;
}

static int DevIfFastRead(int idx, void *buff, size_t size)
{
    fsal_file_t *file;
    devfs_file_extension_t *extension;
    device_object_t *devobj;
    iostatus_t status;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    file = FSAL_FILE_IDX2FILE(idx);
    if (!file)
        return -1;
    extension=file->extension;
    devobj = GET_DEVICE_BY_HANDLE(extension->handle);
    if (!devobj)
        return -1;
    status = FastIoCallDriver(devobj, size, buff, IOREQ_FASTREAD);
    if (status != IO_SUCCESS)
        return -1;
    return 0;
}

static int DevIfFastWrite(int idx, void *buff, size_t size)
{
    fsal_file_t *file;
    devfs_file_extension_t *extension;
    device_object_t *devobj;
    iostatus_t status;

    if (FSAL_FILE_BAD_IDX(idx))
        return -1;
    file = FSAL_FILE_IDX2FILE(idx);
    if (!file)
        return -1;
    extension=file->extension;
    devobj = GET_DEVICE_BY_HANDLE(extension->handle);
    if (!devobj)
        return -1;
    status = FastIoCallDriver(devobj, size, buff, IOREQ_FASTWRITE);
    if (status != IO_SUCCESS)
        return -1;
    return 0;
}

static int FsalDevFsMount(const char *source, const char *targe, char *fstype, uint32_t flags)
{
    if (strcmp(fstype, "devfs"))
    {
        KPrint("[devfs] mount type no valid!\n");
        return -EINVAL;
    }
    if (FsalPathInsert(&devfs_fsal, source, DEVFS_PATH, targe) < 0)
    {
        KPrint("[devfs] mount %s to %s failed!\n", source, targe);
        return -EINVAL;
    }
    devfs_ctime = WALLTIME_WR_TIME(walltime.hour, walltime.minute, walltime.second);
    devfs_cdate = WALLTIME_WR_DATE(walltime.year, walltime.month, walltime.day);
    return 0;
}

static int FsalDevFsUnMount(const char *source, char *path, uint32_t flags)
{
    if (FsalPathRemove(path) < 0)
    {
        KPrint("[devfs] unmount %s failed!\n", source);
        return -1;
    }
    return 0;
}

static int FsalDevFsOpenDir(const char *path)
{
    char *p = DevFsPathTranslate(path);
    if (!p)
    {
        KPrint(PRINT_ERR "devfs path %s translate failed\n");
        return -1;
    }
    // devfs no sub dir
    if (*p != '\0')
    {
        KPrint(PRINT_ERR "devfs: no can have sub dir\n");
        return -1;
    }
    fsal_dir_t *pdir = FsalDirAlloc();
    if (!pdir)
        return -1;
    pdir->extension = KMemAlloc(sizeof(devfs_dir_extension_t));
    if (!pdir->extension)
    {
        FsalDirFree(pdir);
        return -ENOMEM;
    }
    pdir->fsal = &devfs_fsal;

    devfs_dir_extension_t *extension = pdir->extension;
    memset(&extension->devent, 0, sizeof(devent_t));
    extension->curptr = NULL;
    return FSAL_DIR2IDX(pdir);
}

static int FsalDevFsCloseDir(int idx)
{
    fsal_dir_t *pdir = FSAL_IDX2DIR(idx);

    if (FSAL_BAD_DIR_IDX(idx))
        return -EINVAL;
    if (FSAL_BAD_DIR(pdir))
        return -EINVAL;
    if (pdir->extension)
        KMemFree(pdir->extension);
    pdir->extension = NULL;
    if (FsalDirFree(pdir))
        return -1;
    return 0;
}

static int FsalDevFsReadDir(int idx, dirent_t *buff)
{
    fsal_dir_t *dir = FSAL_IDX2DIR(idx);
    devfs_dir_extension_t *extension;
    device_object_t *device;
    dirent_t *dirent = buff;

    if (FSAL_BAD_DIR_IDX(idx))
        return -1;
    if (FSAL_BAD_DIR(dir))
        return -1;
    extension = dir->extension;

    if (SysScanDev(extension->curptr, DEVICE_TYPE_ANY, &extension->devent) < 0)
        return -1;

    if (extension->devent.dev_name[0] == '\0')
        return -1;

    // get device
    device = IoSearchDeviceByName(extension->devent.dev_name);
    if (!device)
    {
        KPrint(PRINT_ERR "devfs: device %s not found!\n", extension->devent.dev_name);
    }
    extension->curptr = &extension->devent;

    switch (device->type)
    {
    case DEVICE_TYPE_DISK:
    case DEVICE_TYPE_VIRTUAL_DISK:
        dirent->attr |= DIRENT_BLOCK;
        break;
    default:
        dirent->attr |= DIRENT_CHAR;
        break;
    }

    dirent->size = 0;
    dirent->time = device->mtime;
    dirent->date = device->mdate;
    memset(dirent->name, 0, DEVICE_NAME_LEN);
    memcpy(dirent->name, extension->devent.dev_name, min(strlen(extension->devent.dev_name), DEVICE_NAME_LEN));
    dirent->name[DIR_NAME_LEN - 1] = '\0';
    return 0;
}

static int FsalDevFsRewindDir(int idx)
{
    fsal_dir_t *dir = FSAL_IDX2DIR(idx);
    devfs_dir_extension_t *extension;

    if (FSAL_BAD_DIR_IDX(idx))
        return -1;
    if (FSAL_BAD_DIR(dir))
        return -1;
    if (!dir->flags)
        return -1;
    // clear extension curptr
    extension = dir->extension;
    extension->curptr = NULL;
    memset(&extension->devent, 0, sizeof(devent_t));
    return 0;
}

static int FsalDevFsAcess(const char *path,mode_t mode)
{
    //KPrint("access: path %s mode %x\n",path,mode);

    if(mode==F_OK)
    {
        device_handle_t handle=DevIfOpen(path,0);
        if(handle<0)
            return -1;
        DevIfClose(handle);
        return 0;
    }
    return -1;
}

static int FsalDevFsStatus(const char *path, status_t *buff)
{
    status_t *stu = buff;
    mode_t mode = 0;
    char *p = DevFsPathTranslate(path);

    if (!p)
        return -1;
    // access devfs rootdir
    if (*p == '\0')
    {
        mode = STU_IREAD | STU_IFDIR;
        stu->st_size = 0;
        stu->st_atime = (devfs_cdate << 16) | devfs_ctime;
        stu->st_ctime = stu->st_mtime = stu->st_atime;
    }
    else
    {
        device_object_t *devobj = IoSearchDeviceByName(p);
        if (!devobj)
        {
            KPrint(PRINT_ERR "[devfs] %s: device %s no found\n", __func__, p);
            return -1;
        }
        mode = STU_IREAD | STU_IWRITE;
        switch (devobj->type)
        {
        case DEVICE_TYPE_DISK:
        case DEVICE_TYPE_VIRTUAL_DISK:
            mode |= STU_IFBLK;
            break;
        default:
            mode |= STU_IFCHR;
            break;
        }
        stu->st_size = 0;
        stu->st_atime = (devobj->mdate << 16) | devobj->mtime;
        stu->st_ctime = stu->st_mtime = stu->st_atime;
    }
    stu->st_mode = mode;
    return 0;
}

// create driver workframe
void DriverFrameInit()
{
    // init all device handle to NULL
    for (int i = 0; i < DEVICE_NUM_MAX; i++)
    {
        device_handle_table[i] = NULL;
    }
    // init driver list
    list_init(&driver_list_head);
    // init devif
    memset(&devfs_fsal, 0, sizeof(fsal_t));
    devfs_fsal.name = "devfs";
    devfs_fsal.open = DevIfOpen;
    devfs_fsal.close = DevIfClose;
    devfs_fsal.read = DevIfRead;
    devfs_fsal.write = DevIfWrite;
    devfs_fsal.ioctl = DevIfIoCtl;
    devfs_fsal.lseek = DevIfLSeek;
    devfs_fsal.fsize = DevIfFSize;
    devfs_fsal.ftell = DevIfFTell;
    devfs_fsal.mmap = DevIfMMap;
    devfs_fsal.fastio = DevIfFastIo;
    devfs_fsal.fastread = DevIfFastRead;
    devfs_fsal.fastwrite = DevIfFastWrite;
    devfs_fsal.opendir = FsalDevFsOpenDir;
    devfs_fsal.closedir = FsalDevFsCloseDir;
    devfs_fsal.readdir = FsalDevFsReadDir;
    devfs_fsal.rewinddir = FsalDevFsRewindDir;
    devfs_fsal.status = FsalDevFsStatus;
    devfs_fsal.mount = FsalDevFsMount;
    devfs_fsal.unmount = FsalDevFsUnMount;
    devfs_fsal.access=FsalDevFsAcess;
    KPrint("[driver] init driver frame done.\n");
}

void DriverFrameDump()
{
    driver_object_t *driver;
    device_object_t *device;

    KPrint("[ driver frame dump ]\n");
    list_traversal_all_owner_to_next(driver, &driver_list_head, list)
    {
        KPrint("driver name %s:", driver->name.text);
        list_traversal_all_owner_to_next(device, &driver->device_list, list)
        {
            KPrint(" %s ", device->name.text);
        }
        KPrint("\n");
    }
}